import { ElMessage } from 'element-plus'
import { getRandomUUID } from '@/utils/randomUtil'

type WebSocketEventCallback = (data?: any) => void;

export class WebSocketUtil {
  private readonly url: string;
  private websocket: WebSocket | null;
  private readonly callbacks: {
    onOpen: WebSocketEventCallback | null;
    onMessage: WebSocketEventCallback | null;
    onError: WebSocketEventCallback | null;
    onClose: WebSocketEventCallback | null;
  };
  private reconnectAttempts: number;
  private maxReconnectAttempts: number;
  private reconnectInterval: number;
  private reconnectTimer: number | null = null;
  private shouldReconnect: boolean;

  constructor(url: string) {
    this.url = url
    this.websocket = null
    this.callbacks = {
      onOpen: null,
      onMessage: null,
      onError: null,
      onClose: null
    }
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 3 // 最大重试次数
    this.reconnectInterval = 2000 // 重试间隔时间(ms)
    this.reconnectTimer = null
    this.shouldReconnect = true // 是否自动重连
  }

  /**
   * 连接WebSocket
   */
  connect(): void {
    if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
      console.log('websocket已经连接')
      return
    }

    this.websocket = new WebSocket(this.url) as WebSocket

    this.websocket.onopen = () => {
      console.log('websocket连接成功')
      this.reconnectAttempts = 0 // 重置重连计数器
      if (this.callbacks.onOpen) {
        this.callbacks.onOpen()
      }
    }

    this.websocket.onmessage = (event: MessageEvent) => {
      if (this.callbacks.onMessage) {
        this.callbacks.onMessage(event.data)
      }
    }

    this.websocket.onerror = (error: Event) => {
      ElMessage.error(`WebSocket连接错误: ${error}`)
      if (this.callbacks.onError) {
        this.callbacks.onError(error)
      }
    }

    this.websocket.onclose = (event: CloseEvent) => {
      console.log('websocket关闭连接', event.code, event.reason)

      if (this.callbacks.onClose) {
        this.callbacks.onClose(event)
      }

      // 非正常关闭且需要重连时尝试重连
      if (this.shouldReconnect && (event.code !== 1000)) {
        this.scheduleReconnect()
      }
    }
  }

  /**
   * 安排重连
   */
  private scheduleReconnect(): void {
    if (this.reconnectAttempts >= this.maxReconnectAttempts) {
      ElMessage.warning(`WebSocket已尝试重连${this.maxReconnectAttempts}次，不再重连`)
      return
    }

    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
    }

    this.reconnectAttempts++
    const delay = this.calculateReconnectDelay()

    console.log(`第${this.reconnectAttempts}次尝试重连，${delay}ms后执行...`)

    this.reconnectTimer = window.setTimeout(() => {
      console.log('正在尝试重新连接...')
      this.connect()
    }, delay)
  }

  /**
   * 计算重连延迟时间（指数退避算法）
   */
  private calculateReconnectDelay(): number {
    // 使用指数退避算法，但不超过reconnectInterval的最大值
    return Math.min(this.reconnectInterval, 1000 * Math.pow(2, this.reconnectAttempts))
  }

  /**
   * 发送消息
   * @param message 消息
   */
  send(message: string): void {
    if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
      ElMessage.error('WebSocket未连接')
      // 可以在这里选择自动重连
      if (this.shouldReconnect) {
        this.connect()
      }
      return
    }
    this.websocket.send(message)
  }

  /**
   * 注册回调函数
   * @param event 事件
   * @param callback 回调函数
   */
  on(event: 'onOpen' | 'onMessage' | 'onError' | 'onClose', callback: WebSocketEventCallback): void {
    this.callbacks[event] = callback
  }

  /**
   * 关闭WebSocket连接
   * @param permanent 是否永久关闭（不自动重连）
   */
  close(permanent = false): void {
    this.shouldReconnect = !permanent
    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer)
      this.reconnectTimer = null
    }
    if (this.websocket) {
      this.websocket.close(1000, 'Manual close')
      this.websocket = null
    }
  }

  /**
   * 判断WebSocket是否已连接
   */
  isConnected(): boolean {
    return this.websocket !== null && this.websocket.readyState === WebSocket.OPEN
  }

  /**
   * 设置最大重连次数
   * @param attempts 次数
   */
  setMaxReconnectAttempts(attempts: number): void {
    this.maxReconnectAttempts = attempts
  }

  /**
   * 设置重连间隔时间
   * @param interval 时间(ms)
   */
  setReconnectInterval(interval: number): void {
    this.reconnectInterval = interval
  }

  formatMessage(tag: string, payload: object, version = 1):string {
    return JSON.stringify({
      id: getRandomUUID(8),
      timestamp: Math.floor(Date.now() / 1000),
      payload,
      tag,
      version
    })
  }
}
